# -*- coding: utf-8 -*-
from ctypes import *
import socket
import struct
import os

def format_output(stream_buff = None, sizeof_eachline = 16):
    import binascii
    #sizef_each 为可选，定义每一行显示多少个字节的数据，一定要为8的倍数
    res = []
    temp_str = []
    for i, j in enumerate(stream_buff):
        #如果j为特殊字符或者超出ascii码，则用"."代替来显示
        if ord(j) < 32 or ord(j)>126:
            temp_str.append('.')
        else:
            temp_str.append(j)
        #每行输出16个字节的16进制和对应的ascii码值
        if i!=0 and i%sizeof_eachline == 0:
            res.append(' '*8)
            res.append(''.join(temp_str))
            temp_str = []
            res.append('\n')
        #每8个字节后，空3格
        elif i!=0 and i%8 == 0:
            res.append(' '*3)
        res.append(' ' + binascii.b2a_hex(j))
    #下面处理最后一行   space_count为16进制数据内容与buff内容显示隔开的空格数
    a, b = divmod(len(stream_buff)%sizeof_eachline, 8)
    if len(stream_buff)%sizeof_eachline == 0:
        space_count = 0
    elif not b:
        space_count = (sizeof_eachline - len(stream_buff) % sizeof_eachline + sizeof_eachline / 8 - a) * 3
    else:
        space_count = (sizeof_eachline - len(stream_buff) % sizeof_eachline + sizeof_eachline / 8 - a - 1) *3
    res.append(' '*(space_count+8) + ''.join(temp_str))
    return ''.join(res)

class pcapHeaderParser(Structure):
    _fields_ = [('magic', c_uint),
                ('ver_major', c_uint, 16),
                ('ver_minor', c_uint, 16),
                ('thiszone', c_int),
                ('sigfigs', c_uint),
                ('snaplen', c_uint),
                ('linktype', c_uint)
                ]
    def __init__(self, buff):
        pass
    def __new__(self, socket_buffer = None):
        return self.from_buffer_copy(socket_buffer)
    def check(self):
        if self.magic == 0xd4c3b2a1:
            self.seqtype = "little-endian"
            assert self.linktype == 0x01000000
            assert self.ver_minor == 0x0400
            assert self.ver_major == 0x0200
        elif self.magic == 0xa1b2c3d4:
            self.seqtype = "big-endian"
            assert self.linktype == 1
            assert self.ver_minor == 4
            assert self.ver_major == 2
        else:
            raise "unsupport file!"


class ipHeaderParser(Structure):
    _fields_ = [('ihl',                  c_ubyte,      4),
                ('version',              c_ubyte,      4),
                ('tos',                  c_ubyte),
                ('len',                  c_ushort),
                ('id',                   c_ushort),
                ('offset',               c_ushort,     12),
                ('flags',                c_ushort,      3),
                ('ttl',                  c_ubyte),
                ('protocol',             c_ubyte),
                ('headerChecksum',       c_ushort),
                ('srcIP',                c_ulong),
                ('dstIP',                c_ulong)]
    def __init__(self, socket_buffer = None):
        protocol_map = {1: 'ICMP', 6: 'TCP', 17: 'UDP'}
        self.proto = protocol_map.get(self.protocol) if protocol_map.get(self.protocol) else str(self.protocol)
        self.src_ip = socket.inet_ntoa(struct.pack('<L', self.srcIP))
        self.dst_ip = socket.inet_ntoa(struct.pack('<L', self.dstIP))
        self.length = struct.unpack('>H', struct.pack('<H', self.len))[0]
    def __new__(self, socket_buffer = None):
        return self.from_buffer_copy(socket_buffer)


class pcapParser():
    def __init__(self, filename):
        self.filename = filename
        self.pcapfd = self.get_pcapfd()
        self.pcaplen = os.path.getsize(filename)
        self.seqtype = 'big-endian'
    def get_pcapfd(self):
        try:
            return open(self.filename, 'rb')
        except file.errors:
            return
    def check_pcap_version(self):
        if self.pcapfd:
            phbuff = self.pcapfd.read(24)
            ph = pcapHeaderParser(phbuff)
            try:
                ph.check()
                self.seqtype = ph.seqtype
            except AssertionError:
                print "pcap file is not be supported!", repr(phbuff)
                raise
    def go_parser(self):
        self.check_pcap_version()
        while self.pcapfd.tell() < self.pcaplen:
            self.packet_buff_handle(self.get_packetbuff())
    def get_packetbuff(self):
        if self.seqtype == 'big-endian':
            ts, caplen, reallen = struct.unpack('q2i', self.pcapfd.read(16))
        else:
            ts, caplen, reallen = struct.unpack('!q2i', self.pcapfd.read(16))
        if caplen == reallen:
            return self.pcapfd.read(caplen)
        else:
            print 'capture incomplete'
            return
    @staticmethod
    def packet_buff_handle(buff):
        if buff[12:14] == '\x08\x00':
            ipheader = ipHeaderParser(buff[14:34])
            print ipheader.length
            print '%s->%s %s'%(ipheader.src_ip, ipheader.dst_ip, ipheader.proto)
        else:
            pass
    def exit(self):
        self.__del__()
    def __del__(self):
        if self.pcapfd:
            self.pcapfd.close()

def print_pcap():
    f = open('aaa.pcap', 'rb')
    print format_output(f.read(), 32)
    f.close()

if __name__ == "__main__":
    #print_pcap()
    f = pcapParser('cookiebase.pcap')
    f.go_parser()



